using System;
using System.Reflection;
using System.Collections.Generic;

namespace Crainiate.Data.Persistence.Cache
{
    //Strongly typed key value pair collection for ReflectionCache items
    public sealed class ReflectionCache: Dictionary<Type, ReflectionCacheItem>
    {
        private List<ReflectionParserBase> _parsers; 

        //Constructors
        public ReflectionCache()
        {
            //Bootstrap any ReflectionParsers
            _parsers = new List<ReflectionParserBase>();

            //Check for any loaded assemblies with ReflectionParser
            Assembly[] assemblyArray = AppDomain.CurrentDomain.GetAssemblies();

            foreach (Assembly assembly in assemblyArray)
            {
                if (!assembly.FullName.StartsWith("mscorlib") && !assembly.FullName.StartsWith("System"))
                {
                    foreach (Type type in assembly.GetTypes())
                    {
                        //Create a new instance of the type
                        if (typeof(ReflectionParserBase).IsAssignableFrom(type) && !type.IsAbstract)
                        {
                            //Check that the parser doesnt already exist
                            if (GetParser(type) != null) throw new ReflectionCacheException("A parser for type " + type.FullName.ToString() + " already exists.");

                            ReflectionParserBase parser = (ReflectionParserBase) Activator.CreateInstance(type);

                            //Check any custom parsers have types defined
                            if (parser.GetType() != typeof(ReflectionParser))
                            {
                                if (parser.Types == null || parser.Types.Length == 0) throw new ReflectionCacheException("Parser " + type.FullName.ToString() + " does not define any types. Use the Types property to define the types for this parser." );
                            }
                            
                            _parsers.Add(parser);
                        }
                    }
                }
            }
        }

        //Properties
        public List<ReflectionParserBase> Parsers
        {
            get
            {
                return _parsers;
            }
        }

        //Methods
        public ReflectionCacheItem Lookup(Type type)
        {
            if (type == null) throw new ReflectionCacheException("Type may not be null when retrieving a cache item.");

            ReflectionCacheItem item = null;
            
            //Try get the item from cache
            if (this.TryGetValue(type, out item))
            {
                return item;
            }
            else
            {
                //Get the correct parser and parse, use default if required
                ReflectionParser parser = GetParser(type);

                if (parser == null) parser = new ReflectionParser();
                
                //Reset, validate and parse the type
                parser.Reset();
                parser.Parse(type);
                parser.Validate(type);

                //Create a new cache item with the parser values
                item = new ReflectionCacheItem(type, parser);

                //Add to the reflection cache
                Add(type, item);
            }

            return item;
        }

        private ReflectionParser GetParser(Type type)
        {
            foreach (ReflectionParser parser in _parsers)
            {
                foreach (Type loop in parser.Types)
                {
                    if (loop.IsAssignableFrom(type)) return parser;
                }
            }

            return null;
        }
    }
}
